package com.hw.backapi.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hw.backapi.context.UserAuthContext;
import com.hw.backapi.service.IAdminService;
import com.hw.common.contants.PageInfoContants;
import com.hw.common.contants.RedisKeyContants;
import com.hw.common.contants.RedisTtlContants;
import com.hw.common.contants.UserContants;
import com.hw.common.enums.DeletedStatusEnum;
import com.hw.common.enums.RoleEnums;
import com.hw.common.exception.BaseException;
import com.hw.common.utils.IpUtil;
import com.hw.common.utils.JwtUtils;
import com.hw.model.common.PageInfo;
import com.hw.model.dto.PageInfoDTO;
import com.hw.model.dto.user.LoginDTO;
import com.hw.model.dto.user.UserSaveDTO;
import com.hw.model.entity.AdminPO;
import com.hw.model.mapper.AdminMapper;
import com.hw.model.vo.UserInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 后台用户表 服务实现类
 * </p>
 *
 * @author lixiaopeng
 * @since 2022-11-20
 */
@Service
@Slf4j
public class AdminServiceImpl extends ServiceImpl<AdminMapper, AdminPO> implements IAdminService {

    @Resource
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    UserAuthContext userAuthContext;

    @Override
    public HashMap<String,Object> login(LoginDTO loginDTO) throws Exception {
        if( StrUtil.isEmpty(loginDTO.getUserName()) ){
            throw new BaseException("Incorrect user name/password");
        }
        if( StrUtil.isEmpty(loginDTO.getPassword()) ){
            throw new BaseException("Incorrect user name/password");
        }

//        log.info("登录ip检验：用户{}",loginDTO.getUserName());
//        if(!IpUtil.checkChinaIp()){
//            throw new BaseException("当前登录ip不正确");
//        }

        //判断是否存在
        LambdaQueryWrapper<AdminPO> query = new LambdaQueryWrapper<>();
        query.eq(AdminPO::getUsername, loginDTO.getUserName());
        query.eq(AdminPO::getDelFlag,DeletedStatusEnum.NOTDELETE.getCode());
        AdminPO adminPO = this.getOne(query);
        if( null == adminPO ){
            throw new BaseException("Incorrect user name/password");
        }else{
            //存在，登录
            String jwtToken = doLogin(loginDTO, adminPO);

            HashMap<String, Object> map = new HashMap<>();
            map.put("token",jwtToken);
            map.put("role",adminPO.getRole());
            map.put("name",adminPO.getUsername());
            map.put("id",adminPO.getId());
            return map;
        }
    }

    @Override
    public void logout() {
        userAuthContext.logout();
    }

    @Override
    public UserInfoVO getUserInfo() {
        AdminPO userInfo = userAuthContext.getUserInfo();
        return BeanUtil.toBean(userInfo,UserInfoVO.class);
    }

    @Override
    public void updateUserInfo(UserSaveDTO userSaveDTO) {
        if( null == userSaveDTO.getId()
                || StrUtil.isEmpty(userSaveDTO.getPassword())){
            throw new BaseException("Incomplete update parameters");
        }

        if( !userSaveDTO.getPassword().equals(userSaveDTO.getRePassword()) ){
            throw new BaseException("The two passwords are inconsistent");
        }
        //查询用户信息
        AdminPO adminPO = this.getById(userSaveDTO.getId());
        if( null == adminPO ){
            throw new BaseException("user does not exist");
        }

        AdminPO userInfo = userAuthContext.getUserInfo();//登录用户信息

        //如果是当前人自己修改，则需要原密码
        if(adminPO.getId().equals(userInfo.getId())){
            if( StrUtil.isEmpty(userSaveDTO.getSourcePassword()) ){
                throw new BaseException("please enter the original password");
            }
            //判断密码是否正确
            if( !adminPO.getPassword().equals(getMD5Password(userSaveDTO.getSourcePassword())) ){
                throw new BaseException("the original password is incorrect");
            }
        }

        //权限判断:如果不是最大的超管，如果被修改用户权限大于登录用户权限不允许
        if(!userInfo.getId().equals(UserContants.SUPER_ADMIN) && adminPO.getId() != userInfo.getId() && userAuthContext.checkRole( adminPO.getRole(),userInfo.getRole()  ) ){
            throw new BaseException("insufficient permissions");
        }

        //判断密码与原密码是否一致
        if( adminPO.getPPassword().equals( userSaveDTO.getPassword() ) ){
            throw new BaseException("the new password is consistent with the original password");
        }

        adminPO.setPPassword(userSaveDTO.getPassword());
        adminPO.setPassword(getMD5Password(userSaveDTO.getPassword()));
        this.updateById(adminPO);
    }

    @Override
    public void addUserInfo(UserSaveDTO userSaveDTO) {
        if( StrUtil.isEmpty(userSaveDTO.getUsername())
                || null == userSaveDTO.getRole()){
            throw new BaseException("User name/role cannot be empty");
        }

        //权限判断:如果被修改用户权限大于登录用户权限不允许
        AdminPO userInfo = userAuthContext.getUserInfo();//登录用户信息
        if( userAuthContext.checkRole( userSaveDTO.getRole(),userInfo.getRole()  ) ){
            throw new BaseException("Insufficient permissions");
        }

        //判断用户名是否存在
        if( checkRepeatName( userSaveDTO.getUsername() , null ) ){
            throw new BaseException("User name already exists");
        }

        AdminPO adminPO = new AdminPO();
        adminPO.setPassword(getMD5Password(UserContants.DEFAULT_PASSWORD));
        adminPO.setPPassword(UserContants.DEFAULT_PASSWORD);
        adminPO.setUsername(userSaveDTO.getUsername());
        adminPO.setRole(userSaveDTO.getRole());
        adminPO.setCreatedAt(new Date());
        adminPO.setUpdatedAt(new Date());

        try{
            this.save(adminPO);
        }catch (Exception e){
            throw new BaseException("Account Create failed");
        }
    }

    @Override
    public void updateRole(UserSaveDTO userSaveDTO) {
        if( null == userSaveDTO.getId()
                || null == userSaveDTO.getRole()){
            throw new BaseException("User name/role cannot be empty");
        }

        //查询用户信息
        AdminPO adminPO = this.getById(userSaveDTO.getId());
        if( null == adminPO ){
            throw new BaseException("user does not exist");
        }

        AdminPO userInfo = userAuthContext.getUserInfo();//登录用户信息
        //权限判断:如果被修改用户权限大于登录用户权限不允许
        if(!adminPO.getId().equals(userInfo.getId()) && userAuthContext.checkRole( adminPO.getRole(),userInfo.getRole()  ) ){
            throw new BaseException("Insufficient permissions");
        }


        //判断角色是否一致
        if(adminPO.getRole().equals(userSaveDTO.getRole())){
            throw new BaseException("The new role is consistent with the original role");
        }

        adminPO.setRole(userSaveDTO.getRole());
        this.updateById(adminPO);
    }

    @Override
    public PageInfo<UserInfoVO> getUserPageList(PageInfoDTO pageInfoDTO) {
        if( null == pageInfoDTO || null == pageInfoDTO.getPageNum() ){
            pageInfoDTO = new PageInfoDTO();
            pageInfoDTO.setPageNum(PageInfoContants.PAGE_NUM);
            pageInfoDTO.setPageSize(PageInfoContants.PAGE_SIZE);
        }
        Page<AdminPO> page = new Page<>(pageInfoDTO.getPageNum(),pageInfoDTO.getPageSize() );

        LambdaQueryWrapper<AdminPO> query = new LambdaQueryWrapper<>();
        query.eq(AdminPO::getDelFlag, DeletedStatusEnum.NOTDELETE.getCode());

        AdminPO userInfo = userAuthContext.getUserInfo();//登录用户信息
        //如果不是超级管理员，则不允许修改。是超级管理员，不允许修改自己的账号
        if(!userInfo.getRole().equals(RoleEnums.SUPER_ADMIN.getCode())){
            query.lt(AdminPO::getRole,userInfo.getRole());
        }

        query.ne(AdminPO::getId,userInfo.getId());
        query.orderByDesc(AdminPO::getCreatedAt);


        Page<AdminPO> list = this.page(page,query);
        List<UserInfoVO> userInfoVOList = list.getRecords().stream().map(adminPO -> {
            UserInfoVO userInfoVO = BeanUtil.toBean(adminPO, UserInfoVO.class);
            userInfoVO.setLabel(0);
            RoleEnums enumByCode = RoleEnums.getEnumByCode(userInfoVO.getRole());
            if( null != enumByCode ){
                userInfoVO.setRoleName(enumByCode.getDesc());
            }
            return userInfoVO;
        }).collect(Collectors.toList());

        //第一页追加自己的
        if( pageInfoDTO.getPageNum() == 1 ){
            UserInfoVO userInfoVO = BeanUtil.toBean(userInfo, UserInfoVO.class);
            userInfoVO.setLabel(1);
            RoleEnums enumByCode = RoleEnums.getEnumByCode(userInfoVO.getRole());
            if( null != enumByCode ){
                userInfoVO.setRoleName(enumByCode.getDesc());
            }
            userInfoVOList.add(0,userInfoVO);
        }

        PageInfo<UserInfoVO> result = new PageInfo<>(pageInfoDTO.getPageNum(),pageInfoDTO.getPageSize());
        result.setPage(list.getTotal(),userInfoVOList);
        return result;
    }

    @Override
    public void delUserInfo(Long id) {
        if( null == id || id < 1 ){
            throw new BaseException("User ID does not exist");
        }
        if( id == 1 ){
            throw new BaseException("User is not allowed to delete");
        }
        AdminPO adminPO = this.getById(id);
        if( null == adminPO ){
            throw new BaseException("user does not exist");
        }
        AdminPO userInfo = userAuthContext.getUserInfo();//登录用户信息
        //如果不是超级管理员，则不允许修改。是超级管理员，不允许修改自己的账号
        if( userInfo.getRole() <= adminPO.getRole() ){
           throw new BaseException("Insufficient permissions");
        }

        adminPO.setUpdatedAt(new Date());
        adminPO.setDelFlag(DeletedStatusEnum.HAVEDELETE.getCode());

        try{
            this.updateById(adminPO);
        }catch (Exception e){
            throw new BaseException("Failed to delete user");
        }

    }

    /**
     * 去登录
     * @param loginDTO
     */
    private String doLogin(LoginDTO loginDTO,AdminPO adminPO) throws Exception {
        //校验密码
        String signPassword = getMD5Password(loginDTO.getPassword());
        if( !signPassword.equals( adminPO.getPassword() ) ){
            throw new BaseException("Incorrect user name/password");
        }

        //登录成功，生成token
        String token = generateToken();

        String jwtToken = JwtUtils.getJwtToken(token);

        //redis存储
        stringRedisTemplate.opsForValue().setIfAbsent(RedisKeyContants.TOKEN + token, JSONUtil.toJsonStr(adminPO), RedisTtlContants.LOGIN, TimeUnit.MINUTES);

        adminPO.setLastIp(IpUtil.getIpAddress());
        adminPO.setUpdatedAt(new Date());
        this.updateById(adminPO);

        return jwtToken;
    }

    /**
     * 生成token
     * @return
     */
    private String generateToken(){
        return UUID.randomUUID().toString();
    }

    /**
     * 生成加密密码
     * @param password
     * @return
     */
    private String getMD5Password(String password){
        return SecureUtil.md5(SecureUtil.sha1("hw2" + password));
    }

    /**
     * 检查名称是否重复 ： true 重复 false 不重复
     * @param username
     * @return
     */
    private Boolean checkRepeatName(String username, Long id) {
        if (StrUtil.isEmpty(username)) {
            return false;
        }
        LambdaQueryChainWrapper<AdminPO> query = this.lambdaQuery();
        query.eq(AdminPO::getUsername, username);
        query.select(AdminPO::getId);
        query.eq(AdminPO::getDelFlag,DeletedStatusEnum.NOTDELETE.getCode());
        query.last("limit 1");
        AdminPO adminPO = this.getOne(query.getWrapper());
        if (null == adminPO) {
            return false;
        }
        if (adminPO.getId().equals(id)) {
            return false;
        }
        return true;
    }
}
